package com.sl.crawler.robot;

import com.sl.crawler.common.enums.crawler.EmProcessState;
import com.sl.crawler.common.utils.DateUtils;
import com.sl.crawler.common.utils.file.FilePathUtils;
import com.sl.crawler.common.utils.thread.ThreadUtils;
import com.sl.crawler.common.utils.yaml.YamlUtils;
import com.sl.crawler.robot.downloader.BaseDownloader;
import com.sl.crawler.robot.downloader.selenium.SeleniumDownloader;
import com.sl.crawler.robot.getUrl.GetUrl;
import com.sl.crawler.robot.output.IOutput;
import com.sl.crawler.robot.parse.IParse;
import com.sl.crawler.robot.parse.impl.StringParse;
import com.sl.crawler.robot.request.Request;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created by shiling on 2017/5/26.
 */
public class Crawler {
    private final static Logger LOG = LoggerFactory.getLogger(Crawler.class);
    private static Map<String, AtomicInteger> processStateMap = new HashMap<>();

    /**
     * 一次只执行一个爬虫
     *
     * @throws Exception
     */
    public static void crawlerSingle(String configPath) {
        AtomicInteger processState = getProcessState(configPath);
        if (!processState.compareAndSet(EmProcessState.STOP.getValue(), EmProcessState.RUNNING.getValue())) {
            LOG.info("抓取任务运行中,直接返回!");
            return;
        }
        try {
            String absolutePath = FilePathUtils.getAbsolutePath(configPath);
            CrawlerConfig crawlerConfig = YamlUtils.loadObject(absolutePath, CrawlerConfig.class);
            String outputCsvPath = crawlerConfig.getOutputCsvPath();
            if (StringUtils.isNotBlank(outputCsvPath) && !StringUtils.endsWithIgnoreCase(outputCsvPath, ".csv")) {
                crawlerConfig.setOutputCsvPath(outputCsvPath + DateUtils.format(new Date(), DateUtils.yyyyMMdd) + ".csv");
            }

            crawler(crawlerConfig);
        } catch (Exception e) {
            LOG.error("抓取失败!", e);
        } finally {
            processState.set(EmProcessState.STOP.getValue());
        }
    }

    /**
     * 一次只执行一个爬虫
     *
     * @throws Exception
     */
    public static void crawler(CrawlerConfig crawlerConfig) throws Exception {
        GetUrl getUrl = new GetUrl();
        getUrl.addToUrlQueue(crawlerConfig.getStartCrawlerUrl());

        BaseDownloader baseDownloader = new SeleniumDownloader();
        IParse iParse = new StringParse();
        crawler(baseDownloader, getUrl, iParse, crawlerConfig);
    }

    public static void crawler(BaseDownloader baseDownloader, GetUrl getUrl, IParse iParse, CrawlerConfig crawlerConfig) throws Exception {
        // 1 获取地址
        int i = 0;
        Request request = getUrl.getUrlQueue().poll();
        while (request != null) {
            i++;
            String url = request.getUrl();
            LOG.info("开始抓取url=" + url);
            crawlerOne(baseDownloader, getUrl, iParse, crawlerConfig, request);

            request = getUrl.getUrlQueue().poll();
            LOG.info("抓取完成个数={},url={}", i, url);
            ThreadUtils.sleepSecond10();
        }
        baseDownloader.getBaseClientPool().closeAll();
        LOG.info("全部抓取完成,抓取的总页面数={}", i);
    }

    private static void crawlerOne(BaseDownloader baseDownloader, GetUrl getUrl, IParse iParse, CrawlerConfig crawlerConfig, Request request) {
        try {
            // 2 下载
            String pageSource = baseDownloader.download(request);
            // 3 解析
            List<Map<String, String>> resultList = iParse.parse(crawlerConfig.getParseConfig(), pageSource);
            // 4 保存
            for (IOutput output : crawlerConfig.getOutputList()) {
                output.output(resultList, crawlerConfig);
            }

            if (pageSource != null) {
                getUrl.process(crawlerConfig, request, pageSource);
            }
        } catch (Exception e) {
            LOG.error("抓取失败url=" + request.getUrl(), e);
        }
    }

    /**
     * 获取状态
     *
     * @param configPath
     * @return
     */
    private static AtomicInteger getProcessState(String configPath) {
        AtomicInteger processState = processStateMap.get(configPath);
        if (processState == null) {
            initProcessStateMap(configPath);
            processState = processStateMap.get(configPath);
        }
        return processState;
    }


    /**
     * 初始化状态列表
     *
     * @param configPath
     */
    public static synchronized void initProcessStateMap(String configPath) {
        AtomicInteger processState = processStateMap.get(configPath);
        if (processState == null) {
            processStateMap.put(configPath, new AtomicInteger(EmProcessState.STOP.getValue()));
        }
    }

}
